﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Application.Entity;
using Admin.NET.Application.Helper;
using Admin.NET.Application.Service.ModbusTcp;
using Admin.NET.Core.Service;
using DotNetty.Buffers;
using DotNetty.Transport.Channels;
using Microsoft.Extensions.Logging;
using NewLife;
using NewLife.Data;
using NewLife.Net.Modbus1;
using System.Net;
using System.Text;

namespace Admin.NET.Application.Service.ModbusTcpService;

public partial class ModbusTcpService
{
    public class MyMaster
    {
        public MyMaster()
        {
        }

        /// <summary>主机地址。用于485编码</summary>
        public Byte Host { get; set; } = 1;

        /// <summary>发送数据后接收数据前的延迟时间，默认0毫秒</summary>
        public Int32 Delay { get; set; }

        public IChannelHandlerContext ChannelHandlerContext { get; set; }

        public bool IsReplied { get; set; }
        public bool IsSendToDevice { get; set; } = false;
        public ModbusEntity ModbusEntity { get; set; }

        #region 方法

        /// <summary>处理指令</summary>
        /// <param name="entity">指令实体</param>
        /// <param name="expect">预期返回数据长度</param>
        /// <returns></returns>
        private ModbusEntity Process(ModbusEntity entity, Int32 expect)
        {
            if (ChannelHandlerContext == null) return null;
            entity.Host = Host;
            // 发送
            var buf = entity.ToArray();
            // Modbus加锁，防止冲突

            IsReplied = false;
            IsSendToDevice = true;
            ModbusEntity = null;
            // 预期返回指令长度，传入参数expect没有考虑头部和校验位
            //var st = Transport as SerialTransport;
            //if (st != null) st.FrameSize = expect + ModbusEntity.NO_DATA_LENGTH;
            //获取ip
            var ip = ((IPEndPoint)ChannelHandlerContext.Channel.RemoteAddress).Address.ToString();
            var logger = App.GetService<ILogger<ModbusTcpService>>();
            logger.LogInformation($"ip：{ip} 发送指令：{buf.ToHex()}");

            AsyncToSync.RunSync(() => { return ChannelHandlerContext.WriteAndFlushAsync(Unpooled.CopiedBuffer(buf)); });
            // lscy 2013-7-29
            // 发送后，休眠一段时间，避免设备数据未全部写到串口缓冲区中
            // 一般情况下，100ms 已足够
            //延时100ms看是否返回
            var dt = DateTime.Now;
            while (!IsReplied)
            {
                Thread.Sleep(5);
                if (DateTime.Now.Subtract(dt).TotalMilliseconds > 3000)
                    throw new Exception($"ip：{ip} 通讯超时");
            }
            IsSendToDevice = false;
            // 读取
            var rs = ModbusEntity;
            if (rs == null) return null;
            if (rs.IsException) throw new ModbusException(rs.Data != null && rs.Data.Length > 0 ? (Errors)rs.Data[0] : 0);
            return rs;
        }

        /// <summary>处理指令</summary>
        /// <param name="entity">指令实体</param>
        /// <param name="expect">预期返回数据长度</param>
        /// <returns></returns>
        public void ProcessForReport(ModbusEntity entity)
        {
            if (ChannelHandlerContext == null) throw new NullReferenceException("ChannelHandlerContext");
            var rspo = GetReportResponse(entity);

            App.GetService<ILogger<ModbusTcpService>>().LogInformation("主动上报答复：" + rspo.ToHex());
            //entity.Host = Host;
            // Modbus加锁，防止冲突
            // lock (this)
            {
                //回答

                ChannelHandlerContext.WriteAndFlushAsync(Unpooled.CopiedBuffer(rspo));
            }
        }

        #endregion 方法

        #region 线圈

        /// <summary>读取线圈状态</summary>
        /// <remarks>
        /// 请求：0x01|2字节起始地址|2字节线圈数量(1~2000)
        /// 响应：0x01|1字节字节计数|n字节线圈状态（n=输出数量/8，如果余数不为0，n=n+1）
        /// </remarks>
        /// <param name="addr"></param>
        /// <returns></returns>
        public Boolean ReadCoil(Int32 addr)
        {
            // 小心内部可能没有返回
            var rs = ReadInputs(MBFunction.ReadCoils, addr, 1);
            if (rs == null || rs.Length < 1) return false;
            return rs[0];
        }

        /// <summary>读取线圈状态</summary>
        /// <remarks>
        /// 请求：0x01|2字节起始地址|2字节线圈数量(1~2000)
        /// 响应：0x01|1字节字节计数|n字节线圈状态（n=输出数量/8，如果余数不为0，n=n+1）
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        public Boolean[] ReadCoils(Int32 addr, UInt16 count)
        {
            return ReadInputs(MBFunction.ReadCoils, addr, count);
        }

        /// <summary>读取离散量输入</summary>
        /// <remarks>
        /// 请求：0x02|2字节起始地址|2字节输入数量(1~2000)
        /// 响应：0x02|1字节字节计数|n字节输入状态（n=输入数量/8，如果余数不为0，n=n+1）
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        public Boolean[] ReadInputs(Int32 addr, UInt16 count)
        {
            return ReadInputs(MBFunction.ReadInputs, addr, count);
        }

        private Boolean[] ReadInputs(MBFunction func, Int32 addr, UInt16 count)
        {
            var cmd = new ModbusEntity
            {
                Function = func
            };
            var buf = new Byte[4];
            buf.WriteUInt16(0, addr);
            buf.WriteUInt16(2, count);
            cmd.Data = buf;

            var rLen = 1 + count / 8;
            if (count % 8 != 0) rLen++;
            var rs = Process(cmd, rLen);
            if (rs == null || rs.Data == null || rs.Data.Length < 1) return null;

            // 特殊处理单个读取，提高效率
            if (count == 1) return new Boolean[] { rs.Data[1] == 1 };

            var flags = new Boolean[count];

            // 元素存放于m字节n位
            Int32 m = 0, n = 0;
            for (var i = 0; i < flags.Length && 1 + m < rs.Data.Length; i++)
            {
                if (((rs.Data[1 + m] >> n) & 0x01) == 1) flags[i] = true;
                if (++n >= 8)
                {
                    m++;
                    n = 0;
                }
            }

            return flags;
        }

        /// <summary>写单个线圈</summary>
        /// <remarks>
        /// 请求：0x05|2字节输出地址|2字节输出值（0x0000/0xFF00）
        /// 响应：0x05|2字节输出地址|2字节输出值（0x0000/0xFF00）
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="flag"></param>
        /// <returns></returns>
        public Boolean WriteSingleCoil(Int32 addr, Boolean flag)
        {
            var cmd = new ModbusEntity
            {
                Function = MBFunction.WriteSingleCoil
            };
            var buf = new Byte[4];
            buf.WriteUInt16(0, addr);
            if (flag) buf.WriteUInt16(2, 0xFF00);
            cmd.Data = buf;

            var rs = Process(cmd, 2 + 2);
            if (rs == null) return false; ;

            return (rs.Data.ReadUInt16(2) != 0) == flag;
        }

        /// <summary>写多个线圈</summary>
        /// <remarks>
        /// 请求：0x0F|2字节起始地址|2字节输出数量（1~1698）|1字节字节计数|n字节输出值（n=输出数量/8，如果余数不为0，n=n+1）
        /// 响应：0x0F|2字节起始地址|2字节输出数量
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="flags"></param>
        /// <returns></returns>
        public Boolean WriteMultipleCoils(Int32 addr, params Boolean[] flags)
        {
            var cmd = new ModbusEntity
            {
                Function = MBFunction.WriteMultipleCoils
            };

            var n = flags.Length / 8;
            if (flags.Length % 8 != 0) n++;

            var buf = new Byte[4 + 1 + n];
            buf.WriteUInt16(0, addr);
            buf.WriteUInt16(2, (UInt16)flags.Length);

            buf[4] = (Byte)n;

            // 元素存放于m字节n位
            var m = n = 0;
            for (var i = 0; i < flags.Length; i++)
            {
                if (flags[i]) buf[5 + m] |= (Byte)(1 << n);

                if (++n >= 8)
                {
                    m++;
                    n = 0;
                }
            }

            cmd.Data = buf;

            var rs = Process(cmd, 2 + 2);
            if (rs == null) return false;

            return rs.Data.ReadUInt16(0) == addr && rs.Data.ReadUInt16(2) == flags.Length;
        }

        #endregion 线圈

        #region 寄存器

        /// <summary>读取保持寄存器</summary>
        /// <remarks>
        /// 请求：0x03|2字节起始地址|2字节寄存器数量（1~2000）
        /// 响应：0x03|1字节字节数|n*2字节寄存器值
        /// </remarks>
        /// <param name="addr"></param>
        /// <returns></returns>
        public UInt16 ReadHoldingRegister(Int32 addr)
        {
            return ReadRegisters(MBFunction.ReadHoldingRegisters, addr, 1)[0];
        }

        /// <summary>读取保持寄存器</summary>
        /// <remarks>
        /// 请求：0x03|2字节起始地址|2字节寄存器数量（1~2000）
        /// 响应：0x03|1字节字节数|n*2字节寄存器值
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        public UInt16[] ReadHoldingRegisters(Int32 addr, UInt16 count)
        {
            return ReadRegisters(MBFunction.ReadHoldingRegisters, addr, count);
        }

        /// <summary>读取输入寄存器</summary>
        /// <remarks>
        /// 请求：0x04|2字节起始地址|2字节输入寄存器数量（1~2000）
        /// 响应：0x04|1字节字节数|n*2字节输入寄存器
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="count">数量</param>
        /// <returns></returns>
        public UInt16[] ReadInputRegisters(Int32 addr, UInt16 count)
        {
            return ReadRegisters(MBFunction.ReadInputRegisters, addr, count);
        }

        private UInt16[] ReadRegisters(MBFunction func, Int32 addr, UInt16 count)
        {
            var cmd = new ModbusEntity
            {
                Function = func
            };
            var buf = new Byte[4];
            buf.WriteUInt16(0, addr);
            buf.WriteUInt16(2, count);
            cmd.Data = buf;

            var rs = Process(cmd, 1 + count * 2);
            if (rs == null) return null;

            count = rs.Data[0];
            if (1 + count > rs.Data.Length) count = (UInt16)(rs.Data.Length - 1);

            var ds = new UInt16[count / 2];
            for (var i = 0; i < ds.Length; i++)
            {
                ds[i] = rs.Data.ReadUInt16(1 + i * 2);
            }

            return ds;
        }

        /// <summary>写单个寄存器</summary>
        /// <remarks>
        /// 请求：0x06|2字节寄存器地址|2字节寄存器值
        /// 响应：0x06|2字节寄存器地址|2字节寄存器值
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="val"></param>
        /// <returns></returns>
        public Boolean WriteSingleRegister(Int32 addr, UInt16 val)
        {
            var cmd = new ModbusEntity
            {
                Function = MBFunction.WriteSingleRegister
            };
            var buf = new Byte[4];
            buf.WriteUInt16(0, addr);
            buf.WriteUInt16(2, val);
            cmd.Data = buf;

            var rs = Process(cmd, 2 + 2);
            if (rs == null) return false;

            return rs.Data.ReadUInt16(0) == addr && rs.Data.ReadUInt16(2) == val;
        }

        /// <summary>写多个寄存器</summary>
        /// <remarks>
        /// 请求：0x10|2字节起始地址|2字节寄存器数量（1~123）|1字节字节计数|n*2寄存器值
        /// 响应：0x10|2字节起始地址|2字节寄存器数量
        /// </remarks>
        /// <param name="addr"></param>
        /// <param name="vals"></param>
        /// <returns></returns>
        public Boolean WriteMultipleRegisters(Int32 addr, params UInt16[] vals)
        {
            var cmd = new ModbusEntity
            {
                Function = MBFunction.WriteMultipleRegisters
            };

            var buf = new Byte[4 + 1 + vals.Length * 2];
            buf.WriteUInt16(0, addr);
            buf.WriteUInt16(2, (UInt16)vals.Length);
            // 字节计数
            buf[4] = (Byte)(vals.Length * 2);

            for (var i = 0; i < vals.Length; i++)
            {
                buf.WriteUInt16(5 + i * 2, vals[i]);
            }

            cmd.Data = buf;

            var rs = Process(cmd, 2 + 2);
            if (rs == null) return false;

            return rs.Data.ReadUInt16(0) == addr && rs.Data.ReadUInt16(2) == vals.Length;
        }

        #endregion 寄存器

        #region 诊断标识

        /// <summary>诊断</summary>
        /// <remarks>
        /// 01080000801A
        /// 请求：0x08|2字节子功能|n*2字节数据
        /// 响应：0x08|2字节子功能|n*2字节数据
        /// </remarks>
        /// <returns></returns>
        public Boolean Diagnostics()
        {
            var cmd = new ModbusEntity
            {
                Function = MBFunction.Diagnostics
            };

            // 子功能码
            var buf = new Byte[2];
            buf.WriteUInt16(0, 0);
            cmd.Data = buf;

            var rs = Process(cmd, 2);
            return rs != null;
        }

        /// <summary>返回标识</summary>
        /// <remarks>
        /// 0111C02C
        /// </remarks>
        /// <returns></returns>
        public Byte[] ReportIdentity()
        {
            var cmd = new ModbusEntity
            {
                Function = MBFunction.ReportIdentity
            };

            var rs = Process(cmd, 1 + 8);
            if (rs == null) return null;

            var count = (Int32)rs.Data[0];
            if (count > rs.Data.Length - 1) count = rs.Data.Length - 1;

            if (count <= 0) return new Byte[0];

            return NewLife.Net.Modbus1.IOHelper.ReadBytes(rs.Data, 1, count);
        }

        #endregion 诊断标识

        #region 主动上传

        public byte[] GetReportResponse(ModbusEntity modbusEntity)
        {
            var func = modbusEntity.Function;
            var cmd = new ModbusEntity
            {
                Function = func
            };
            cmd.Data = modbusEntity.Data.Take(4).ToArray();
            return cmd.ToArray();
        }

        public bool[] ByteArrayToBoolArray(byte[] byteArray)
        {
            bool[] boolArray = new bool[byteArray.Length * 8];
            for (int i = 0; i < byteArray.Length; i++)
            {
                for (int j = 0; j < 8; j++)
                {
                    boolArray[i * 8 + j] = (byteArray[i] & (1 << j)) != 0;
                }
            }
            return boolArray;
        }

        #endregion 主动上传
    }

    public class ReportEntity
    {
        /// <summary>主机地址。用于485编码</summary>
        public Byte Host { get; set; } = 1;

        /// <summary>功能码</summary>
        public MBFunction Function { get; set; }

        /// <summary>
        /// 开始地址
        /// </summary>
        public int StartAddress { get; set; }

        /// <summary>
        /// 几包数据
        /// </summary>
        public int PacketCount { get; set; }

        /// <summary>
        /// 数据总共多少位
        /// </summary>
        public int DataCount { get; set; }

        /// <summary>
        /// 实际的数据包
        /// </summary>
        public byte[] DataArray { get; set; }

        public bool Parse(ModbusEntity modbusEntity)
        {
            try
            {
                if (modbusEntity == null) return false;
                if (modbusEntity.Data == null || modbusEntity.Data.Length < 6) return false;
                var addrByte = modbusEntity.Data.Take(2).ToArray();
                var addr = addrByte[0] << 8 | addrByte[1];
                var countByte = modbusEntity.Data.Skip(2).Take(2).ToArray();
                var count = countByte[0] << 8 | countByte[1];
                var usefulDataCount = modbusEntity.Data.Skip(4).Take(1).ToArray()[0];
                var data = modbusEntity.Data.Skip(5).Take(usefulDataCount).ToArray();
                //这时候对数据进行校验
                if (data.Length != usefulDataCount)
                {
                    return false;
                }

                Host = modbusEntity.Host;
                Function = modbusEntity.Function;
                StartAddress = addr;
                PacketCount = count;
                DataCount = usefulDataCount;
                DataArray = data;
                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}